package ruleutil

import (
	"fmt"
	"net"
	"strconv"
	"strings"
	"trusteva/app/eva/model"

	"github.com/3th1nk/cidr"
	"github.com/axengine/utils/hash"
	"github.com/pkg/errors"
)

type Rule struct {
	DeviceId string
	AppId    string
	DstIP    string
	DstPort  string
	Status   int
	UserId   string
}

func (r *Rule) Key() string {
	//k := fmt.Sprintf("%s/%s/%s/%s/%s",
	//	r.DeviceId, r.AppId, r.DstIP, r.DstPort, r.UserId)
	//fmt.Println("-原始key:", k)
	return hash.MD5Lower([]byte(fmt.Sprintf("%s%s%s%s%s",
		r.DeviceId, r.AppId, r.DstIP, r.DstPort, r.UserId)))
}

func ParseRules(m model.Rule) ([]Rule, error) {
	// 解析IP列表
	m.DstIP = strings.TrimSpace(m.DstIP)
	ipstrs := strings.Split(m.DstIP, ",")
	var ips = make(map[string]bool)
	for _, v := range ipstrs {
		v = strings.TrimSpace(v)
		if isIP(v) {
			ips[v] = true
		} else if isCIDR(v) {
			c, err := cidr.Parse(v)
			if err != nil {
				return nil, errors.New("invalid targetIP CIDR")
			}
			if c.IPCount().Int64() > 1024 {
				return nil, errors.New("invalid targetIP:CIDR MAX IPCount is 1024")
			}
			c.Each(func(ip string) bool {
				ips[ip] = true
				return true
			})
		} else if isIPAsterisk(v) {
			ipar := IPAsteriskRange(v)
			err := ipar.Each(func(ip string) bool {
				ips[ip] = true
				return true
			})
			if err != nil {
				return nil, err
			}
		}
	}
	if len(ips) == 0 {
		return nil, errors.New("invalid targetIP")
	}

	// 解析端口列表
	var ports = make(map[int]bool)
	m.DstPort = strings.TrimSpace(m.DstPort)
	portstrs := strings.Split(m.DstPort, ",")
	for _, v := range portstrs {
		v = strings.TrimSpace(v)
		begin, end := PortRange(v).Range()
		if begin != 0 && end != 0 {
			for i := begin; i <= end; i++ {
				ports[i] = true
			}
		}
	}

	status := m.Status

	var rules []Rule
	for ip := range ips {
		for port := range ports {
			rules = append(rules, Rule{
				DeviceId: strings.TrimSpace(m.DeviceId),
				AppId:    strings.TrimSpace(m.AppId),
				DstIP:    ip,
				DstPort:  fmt.Sprintf("%d", port),
				Status:   int(status),
				UserId:   strings.TrimSpace(m.UserId),
			})
		}
	}

	return rules, nil
}

// isIPv4 is the validation function for validating if a value is a valid v4 IP address.
func isIPv4(fl string) bool {
	ip := net.ParseIP(fl)

	return ip != nil && ip.To4() != nil
}

// isIPv6 is the validation function for validating if the field's value is a valid v6 IP address.
func isIPv6(fl string) bool {
	ip := net.ParseIP(fl)

	return ip != nil && ip.To4() == nil
}

// isIP is the validation function for validating if the field's value is a valid v4 or v6 IP address.
func isIP(fl string) bool {
	ip := net.ParseIP(fl)

	return ip != nil
}

// isCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address.
func isCIDRv4(fl string) bool {
	ip, _, err := net.ParseCIDR(fl)

	return err == nil && ip.To4() != nil
}

// isCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address.
func isCIDRv6(fl string) bool {
	ip, _, err := net.ParseCIDR(fl)

	return err == nil && ip.To4() == nil
}

// isCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address.
func isCIDR(fl string) bool {
	_, _, err := net.ParseCIDR(fl)

	return err == nil
}

func isPort(fl string) bool {
	port, _ := strconv.ParseInt(fl, 10, 32)
	if port > 0 && port < 65536 {
		return true
	}
	return false
}

type PortRange string

func isPortRange(fl PortRange) bool {
	portRange := strings.Split(string(fl), "/")
	if len(portRange) == 1 {
		return false
	}
	return true
}

func (pr PortRange) Range() (int, int) {
	portRange := strings.Split(string(pr), "/")
	if len(portRange) != 2 {
		return 0, 0
	}
	begin, _ := strconv.ParseInt(portRange[0], 10, 32)
	end, _ := strconv.ParseInt(portRange[1], 10, 32)
	if begin < 1 || begin > 65535 {
		begin = 0
	}
	if end < 1 || end > 65535 {
		end = 0
	}
	return int(begin), int(end)
}

// isIPandAsterisk is the validation function for validating if the field's value is a valid v4 or v6 IP address or asterisk.
func isIPAsterisk(fl string) bool {
	ip := strings.Split(fl, ".")
	if len(ip) != 4 {
		return false
	}
	ip_template := []string{3: ""}
	for i, v := range ip {
		if v == "*" {
			if i == 0 {
				return false
			}
			ip_template[i] = "1"
		} else {
			ip_template[i] = v
		}
	}
	ip_str := strings.Join(ip_template, ".")
	return isIP(ip_str)
}

type IPAsteriskRange string

func (ipar IPAsteriskRange) Each(iterator func(ip string) bool) (err error) {
	// 第一步 分割每一位 如果是*则替换为-1 如果不是*则不变 处理为一个int数组
	ip := strings.Split(string(ipar), ".")
	if len(ip) != 4 {
		return errors.New("invalid targetIP")
	}
	ip_template := []int{3: 0}
	for i, v := range ip {
		if v == "*" {
			if i == 0 {
				return errors.New("invalid targetIP")
			}
			ip_template[i] = -1
		} else {
			ip_template[i], err = strconv.Atoi(v)
			if err != nil {
				return errors.New("convert targetIP failed")
			}
		}
	}

	// 第二步 生成一个int数组的所有组合 并调用迭代器
	ipar.eachOne(ip_template[0], func(ip1 string) {
		ipar.eachOne(ip_template[1], func(ip2 string) {
			ipar.eachOne(ip_template[2], func(ip3 string) {
				ipar.eachOne(ip_template[3], func(ip4 string) {
					ip := strings.Join([]string{ip1, ip2, ip3, ip4}, ".")
					// 第四步 调用迭代器
					if !iterator(ip) {
						return
					}
				})
			})
		})
	})

	return nil
}

func (ipar IPAsteriskRange) eachOne(num int, iterator func(ip string)) {
	if num == -1 {
		for i := 1; i < 256; i++ {
			iterator(strconv.Itoa(i))
		}
	} else {
		iterator(strconv.Itoa(num))
	}
}

func makeAB() *chan string {
	ch := make(chan string)
	go func() {
		for {
			ch <- "A"
			ch <- "B"
		}
	}()
	return &ch
}
